﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Table;
using System.Data.Services.Common;
using Microsoft.WindowsAzure.Storage.Table.DataServices;
using System.Xml.Linq;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml.Serialization;
using System.Reflection;
using System.Data;

namespace POC.Reports
{
    public static class LogEntryReaderExtn
    {
        //static Type  LogEntryType = typeof(LogEntry);
        public static object ReadEntity(this IDictionary<string, EntityProperty> properties, Type objectType)
        {
            object logEntry = Activator.CreateInstance(objectType);
            foreach (PropertyInfo info in objectType.GetProperties())
            {
                EntityProperty property;
                if (info.Name == "PartitionKey" || info.Name == "RowKey" || info.Name == "Timestamp" || info.Name != "ETag")
                    continue;
                if (info.GetSetMethod() == null || !info.GetSetMethod().IsPublic || info.GetGetMethod() == null || !info.GetGetMethod().IsPublic
                     || !properties.ContainsKey(info.Name))
                    continue;
                {

                    property = properties[info.Name];

                    bool isNull = (bool)typeof(EntityProperty).GetProperty("IsNull", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(property, null);
                    if (isNull)
                    {
                        info.SetValue(logEntry, null, null);
                    }
                    else
                    {
                        switch (property.PropertyType)
                        {
                            case EdmType.String:
                                {
                                    if (info.PropertyType == typeof(string))
                                    {
                                        info.SetValue(logEntry, property.StringValue, null);
                                    }
                                    break;
                                }
                            case EdmType.Binary:
                                {
                                    if (info.PropertyType == typeof(byte[]))
                                    {
                                        info.SetValue(logEntry, property.BinaryValue, null);
                                    }
                                    break;
                                }
                            case EdmType.Boolean:
                                {
                                    if (info.PropertyType == typeof(bool) || info.PropertyType == typeof(bool?))
                                    {
                                        info.SetValue(logEntry, property.BooleanValue, null);
                                    }
                                    break;
                                }
                            case EdmType.DateTime:
                                {
                                    if (info.PropertyType == typeof(DateTime))
                                    {
                                        info.SetValue(logEntry, property.DateTimeOffsetValue.Value.UtcDateTime, null);
                                    }
                                    else if (info.PropertyType == typeof(DateTime?))
                                    {
                                        info.SetValue(logEntry, property.DateTimeOffsetValue.HasValue ? property.DateTimeOffsetValue.Value.UtcDateTime : (DateTime?)null, null);
                                    }
                                    else if (info.PropertyType == typeof(DateTimeOffset))
                                    {
                                        info.SetValue(logEntry, property.DateTimeOffsetValue.Value, null);
                                    }
                                    else if (info.PropertyType == typeof(DateTimeOffset?))
                                    {
                                        info.SetValue(logEntry, property.DateTimeOffsetValue, null);
                                    }
                                    break;
                                }
                            case EdmType.Double:
                                {
                                    if (info.PropertyType == typeof(double) || info.PropertyType == typeof(double?))
                                    {
                                        info.SetValue(logEntry, property.DoubleValue, null);
                                    }
                                    break;
                                }
                            case EdmType.Guid:
                                {
                                    if ((info.PropertyType == typeof(Guid)) || (info.PropertyType == typeof(Guid?)))
                                    {
                                        info.SetValue(logEntry, property.GuidValue, null);
                                    }
                                    break;
                                }
                            case EdmType.Int32:
                                {
                                    if (info.PropertyType == typeof(int) || info.PropertyType == typeof(int?))
                                    {
                                        info.SetValue(logEntry, property.Int32Value, null);
                                    }
                                    break;
                                }
                            case EdmType.Int64:
                                {
                                    if (info.PropertyType == typeof(long) || info.PropertyType == typeof(long?))
                                    {
                                        info.SetValue(logEntry, property.Int64Value, null);
                                    }
                                    break;
                                }
                        }
                    }
                }
            }
            return logEntry;
        }

        public static IDictionary<string, EntityProperty> ToProperties(this object logEntry, Type objectType, string[] ColumnsNames = null)
        {
            Dictionary<string, EntityProperty> dictionary = new Dictionary<string, EntityProperty>();
            foreach (PropertyInfo info in objectType.GetProperties())
            {
                if (((!(info.Name == "PartitionKey") && !(info.Name == "RowKey")) && (!(info.Name == "Timestamp") && !(info.Name == "ETag")))
                    && (((info.GetSetMethod() != null) && info.GetSetMethod().IsPublic) && ((info.GetGetMethod() != null) && info.GetGetMethod().IsPublic)))
                {
                    EntityProperty property = CreateEntityPropertyFromObject(info.GetValue(logEntry, null), false);
                    if (property != null)
                    {
                        if (ColumnsNames == null)
                        {
                            dictionary.Add(info.Name, property);
                        }
                        else
                        {
                            bool isColumnToAdd = ColumnsNames.Where(x => x.ToLower() == info.Name.ToLower()).Any();
                            if (isColumnToAdd)
                            {
                                dictionary.Add(info.Name, property);
                            }
                        }
                    }
                }
            }
            return dictionary;
        }

        public static DataRow ToTableRow(this object logEntry, DataTable table, string[] ColumnsNames = null)
        {
            Dictionary<string, EntityProperty> dictionary = new Dictionary<string, EntityProperty>();
            DataRow row = table.NewRow();
            Type objectType = logEntry.GetType();
            foreach (PropertyInfo info in objectType.GetProperties())
            {
                if ((((info.GetSetMethod() != null) && info.GetSetMethod().IsPublic) && ((info.GetGetMethod() != null) && info.GetGetMethod().IsPublic)))
                {
                    EntityProperty property = CreateEntityPropertyFromObject(info.GetValue(logEntry, null), false);
                    if (property != null)
                    {
                        if (ColumnsNames == null)
                        {
                            row[info.Name] = info.GetValue(logEntry, null);
                        }
                        else
                        {
                            bool isColumnToAdd = ColumnsNames.Where(x => x.ToLower() == info.Name.ToLower()).Any();
                            if (isColumnToAdd)
                            {
                                row[info.Name] = info.GetValue(logEntry, null);
                            }
                        }
                    }
                }
            }
            return row;
        }

        public static DataTable ToTableSchema(this object logEntry, string[] ColumnsNames = null)
        {
            Dictionary<string, EntityProperty> dictionary = new Dictionary<string, EntityProperty>();
            Type objectType = logEntry.GetType();
            DataTable table = new DataTable(objectType.Name);
            foreach (PropertyInfo info in objectType.GetProperties())
            {
                if ((((info.GetSetMethod() != null) && info.GetSetMethod().IsPublic) && ((info.GetGetMethod() != null) && info.GetGetMethod().IsPublic)))
                {
                    EntityProperty property = CreateEntityPropertyFromObject(info.GetValue(logEntry, null), false);
                    if (property != null)
                    {
                        if (ColumnsNames == null)
                        {
                            Type pType = info.PropertyType;
                            if (info.PropertyType == typeof(string) || info.PropertyType.IsPrimitive)
                            {
                                pType = info.PropertyType;
                            }
                            else if (info.PropertyType.IsEnum)
                            {
                                pType = typeof(int);
                            }
                            else
                            {
                                pType = typeof(string);
                            }
                            DataColumn cloumn = new DataColumn(info.Name, pType);
                            table.Columns.Add(cloumn);
                        }
                        else
                        {
                            bool isColumnToAdd = ColumnsNames.Where(x => x.ToLower() == info.Name.ToLower()).Any();
                            if (isColumnToAdd)
                            {
                                Type pType = info.PropertyType;
                                if (info.PropertyType == typeof(string) || info.PropertyType.IsPrimitive)
                                {
                                    pType = info.PropertyType;
                                }
                                else if (info.PropertyType.IsEnum)
                                {
                                    pType = typeof(int);
                                }
                                else
                                {
                                    pType = typeof(string);
                                }
                                DataColumn cloumn = new DataColumn(info.Name, pType);
                                table.Columns.Add(cloumn);
                            }
                        }
                    }
                }
            }
            return table;
        }
        private static EntityProperty CreateEntityPropertyFromObject(object value, bool allowUnknownTypes)
        {
            if (value is string)
            {
                return new EntityProperty((string)value);
            }
            if (value is byte[])
            {
                return new EntityProperty((byte[])value);
            }
            if (value is bool)
            {
                return new EntityProperty((bool)value);
            }
            if (value is bool?)
            {
                return new EntityProperty((bool?)value);
            }
            if (value is DateTime)
            {
                return new EntityProperty((DateTime)value);
            }
            if (value is DateTime?)
            {
                return new EntityProperty((DateTime?)value);
            }
            if (value is DateTimeOffset)
            {
                return new EntityProperty((DateTimeOffset)value);
            }
            if (value is DateTimeOffset?)
            {
                return new EntityProperty((DateTimeOffset?)value);
            }
            if (value is double)
            {
                return new EntityProperty((double)value);
            }
            if (value is double?)
            {
                return new EntityProperty((double?)value);
            }
            if (value is Guid?)
            {
                return new EntityProperty((Guid?)value);
            }
            if (value is Guid)
            {
                return new EntityProperty((Guid)value);
            }
            if (value is int)
            {
                return new EntityProperty((int)value);
            }
            if (value is int?)
            {
                return new EntityProperty((int?)value);
            }
            if (value is long)
            {
                return new EntityProperty((long)value);
            }
            if (value is long?)
            {
                return new EntityProperty((long?)value);
            }
            if (value == null)
            {
                return new EntityProperty((string)null);
            }
            if (allowUnknownTypes)
            {
                return new EntityProperty(value.ToString());
            }
            return null;
        }
    } 
}
